PRODUCTION AGING PLATFORM / ENGINEERING DESIGN

Local + DeviceBroker
混合架构详细设计说明书 V3.0

统一业务入口,按物理会话治理连接;以租约授权、任务串行和恢复门禁支撑 120 穴位并发老化。

详细设计评审稿2026-11-03.NET Framework 4.7.2完整离线 HTML
22 / 22 章

01 设计范围与结论

本说明书将老化软件仪器资源管理收敛为“统一业务入口 + Local 直连 + DeviceBroker 代理”的混合架构。120 个穴位拥有独立的 DUT 业务状态,但共享设备按物理连接、通道与协议会话仲裁。工程师通过发布配置选择执行位置,Recipe 使用逻辑能力与穴位映射,不直接选择 COM 口、IP 或进程。

Local 适合已分配给唯一执行进程、可独立连接的设备;Broker 适合跨进程共享的串口/USB/CAN、共用会话的多通道电源、RS485 总线及需要长连接和后台协议任务的设备。Broker 是设备 I/O 宿主,不承担 DUT 合格判定、整条 Recipe 或 MES 事务。

标识设计不变量实现位置
INV-01同一 PhysicalSessionKey 只有一个配置的 I/O 宿主;禁止 Local 与 Broker 同时打开发布校验、连接守卫、操作系统/网络访问控制
INV-02预约授权与命令串行化分层;持有租约不等于可并发写同一物理会话ResourceManager + SessionGate
INV-03租约到期先隔离和确认设备状态,禁止仅按 TTL 自动交给新持有者仲裁状态机、恢复协调器
INV-04启动后冻结 Recipe、Config、Driver、DBC、UDS、映射 HashRunSnapshot、Broker Task
INV-05所有写操作在实际 I/O 宿主执行入口校验;客户端校验仅为提示LocalExecutor / Broker Dispatcher
INV-06命令已发送但结果未知时,记录 Unknown;禁止把超时当作未执行Task Journal、结果投递
INV-07安全急停和人员作业许可依赖独立硬件/PLC 联锁,软件租约不替代断能设备电气与软件联锁接口
状态:详细设计评审稿,2026-11-03。V3.0 是本次设计版本,不代表软件已实现或通过现场验收。硬件型号、CAN 拓扑、节拍与安全动作按第 21 章的适用条件确认。

02 既有讨论、代码事实与修订依据

本次已读取本地 V2.1 设计说明书、多进程资源方案、配置/调试架构,以及下列 C# 文件。保留 V2.1 的 AgingRun / DUTExecution、版本快照、统一 DeviceService 和分层数据归档设计;将此前“按需 Broker”展开成可部署的混合方案。可检索的历史聊天仅提供部分近期内容,因此不将本文表述为全部历史讨论的逐字复现。

来源/证据当前核查事实V3.0 处理
Aging.Infrastructure/Core/ResourceManager.csConcurrentDictionary 保存预约;_generation 为进程内字段;AcquireOperationAsync 未取得可贯穿 I/O 的排他锁;ValidateOwner 未检查 TTL新增持久化 fence、真实 SessionGate 和异步校验;不声称当前代码已解决操作互斥
Contracts/ResourceOwnerToken.csGeneration 可变,ExpiresAt 只读;构造器自动创建新 TokenId新增不可变 LeaseGrant DTO;持久化还原不能调用旧构造器生成新 token
Services/DeviceService.cs先 GetDriver,再 ValidateOwner,再调用驱动;使用 deviceId:Generic按运行快照解析通道资源键,执行入口再次校验;不允许绕开宿主
Services/AutoDeviceFacade.cs释放异常仅记日志;旧接口为单设备作用域增加释放失败状态、续期和原子组预约;保留兼容适配层但不宣称零改动
ConnectionUtil/PipeClient.csSendMessage 在 WriteMessage 后立即 return true,读响应循环不可达;_serverName 未用于创建客户端流只能复用代码框架;先补协议、响应关联、超时、连接生命周期和测试
Plugin.DeviceConfig/Model/DeviceConfigModel.cs已有 DeviceDefinition、DeviceChannel、SlotDeviceMapping、DeviceConfigVersion、DeviceGroupTemplate保留导入来源;新增不可变配置快照,不把旧表主键当作完整历史版本模型
Aging.Infrastructure.csprojTargetFramework=net472,LangVersion=10.0,Microsoft.Bcl.AsyncInterfaces 包已引用接口示例使用普通 DTO / Task;新包按目标框架验证后冻结
MultiProcess_Instrument_Resource_Solution.html提出 DB Lease + Generation + 按需 Broker;含仅查询 DB 即保证物理安全等过强结论将 TTL、软件 fencing、物理断能分开;维护交接必须经过恢复门禁

代码路径基准:D:\TestPlatform\TapProject\。项目文档来源为设计依据,当前 C# 文件为静态实现证据;本次未运行现有设备驱动或完整软件工程。来源清单和 SHA256 见第 22 章。

03 总体架构与依赖边界

图1 V3.0目标架构设计
图 1 统一入口下的两条 I/O 路径;整图为 V3.0 目标设计。
模块职责明确边界
RunService / RecipeScheduler创建 DUTExecution;异步等待;组织步骤、资源集合和恢复策略不持有物理连接、不直接引用厂家 SDK
DeviceService / ResourceRouter逻辑能力解析、权限与快照检查、路由、结构化错误不根据网络故障自动切换 Local/Broker
Local ResourceManager / LocalExecutor同机 arb.db 预约;命名 Mutex 连接守卫;会话队列与直连驱动只控制配置分配给本进程的会话;TTL 过期不强抢
BrokerClient / LeaseAuthorityClient命名管道请求、响应关联、事件订阅、断线查询无真实驱动,不把客户端 UI 状态当授权
DeviceBroker本域授权、任务调度、会话串行、驱动生命周期、遥测分发不替代 Recipe、MES 或全平台全局锁服务
DeviceManager / Driver依配置实例化、连接、自检、重连、协议解析和读回不决定谁有生产/维护权限
ConfigurationService草稿/发布/生效、拓扑冲突检查、版本分发运行中的快照不可热覆盖
Supervisor / RecoveryCoordinator宿主存活检查、隔离、设备核对、人工恢复许可拉起进程不自动恢复输出

ResourceManager 与 DeviceManager 互不直接依赖,由应用协调器组合。Broker 内也采用同样边界,所有物理写入只能从 Dispatcher 经 SessionGate 到 Driver。监控读取缓存快照,不额外建立同一设备连接。

04 Local / Broker 选择、资源键与粒度

设备/行为推荐路径授权资源粒度物理串行粒度
独立单通道 TCP 电源,仅单 Worker 使用Local;若要跨进程维护则经原宿主代理或停机交接psu/PSU001/ch/1 + parent permitPSU001/session/main
8 通道电源,共同 SCPI/串口会话Broker每输出通道独占;整机 reset 取 parent exclusive整机 SessionKey
CAN/CAN-FD 卡,多个 DUT 共用接收Brokerbus permit + 发送规则;UDS 按 bus/address/session 排他SDK 线程安全域;UDS 会话队列
RS485 多地址总线Broker站地址/功能;广播命令取 bus exclusive物理串口 SessionKey
温箱、PLC 温度/联锁Broker 单一配方控制者chamber 控制权按 Run/group;各 DUT 仅订阅PLC/温箱真实连接
共享 DMM + 继电器矩阵Broker 原子任务DMM + Matrix 路由组合切换→稳定→测量→释放片段
模拟设备Local 或仿真 Broker沿用真实资源键与策略仿真注入队列与故障

4.1 三类键

ResourceKey 表示业务可授予的权利;PhysicalSessionKey 表示必须串行化的真实连接;AuthorityId 表示签发与校验租约的唯一主体。地址别名必须规范化为同一物理身份,例如串口号+设备序列号、IP/端口+仪器序列号、CAN 适配器序列号+通道;不能仅用工程师自定义 DeviceId 防重。

本版本 ResourceKey 统一小写、固定分隔符并使用 Ordinal 排序;配置保存时完成规范化。多通道 SessionGate 内必须把“选择通道 + 写入/查询 + 读取响应”视为不可拆分片段,避免两个穴位选错通道。

4.2 父子资源冲突

每个通道持有独占通道令牌及父设备的共享 Permit。整机 reset、断连、改通信参数、维护接线需要父设备独占,必须等待所有 Permit 归还。父 Permit 也进入租约集合和持久化表,不能仅锁一个字符串前缀。V3.0 的 DDL 给出排他叶资源基线;父共享 Permit 使用应用层在同一 BEGIN IMMEDIATE 内检查/更新的 parent_permit 表实现,不使用 SQLite 的行级锁假设。

跨 Authority 的多资源组采用协调式预留:Prepare(有期限、不可 I/O)→全部准备成功→Activate;任一步失败则 Abort。发生协调器崩溃时全部按预留期限失效进入检查。它不能宣称跨库原子事务;要求严格全有全无的动作必须将资源放入同一 Broker 域,或将整组动作作为一个 Broker Task。禁止持有域 A 的运行租约无限等待域 B。

05 工程师配置与发布工作流

沿用 DeviceConfigView、DeviceDebugView、DeviceMonitorView 分工。配置工程师编辑草稿,维护工程师申请维护会话,操作员启动生产。管理员发布权限不自动授予设备写权限。以下为配置工作台字段定义,不代表当前 UI 已实现。

配置页关键字段校验与反馈
设备注册DeviceId、Kind、DriverType/Version/Hash、Transport、EndpointRef、PhysicalIdentity、HostId驱动能力、位数、地址/物理身份去重;凭据只保存引用
执行位置ExecutionMode=Local/Broker、AuthorityId、BrokerId/OwnerProcessId、PhysicalSessionKey同一会话只能有一个宿主;BrokerId 与设备域一致
通道与资源ChannelId、Capabilities、ResourceKey、ParentKey、AccessPolicy、ConflictDomain通道唯一;整机动作与通道动作冲突展开完整
穴位绑定SlotId、Capability、DeviceId、ChannelId、GroupId、MappingVersion120 穴位能力覆盖,无缺失/重复功率通道;共享映射须显式标注
Broker Task 模板TemplateId/Version、OpCode 白名单、周期、deadline、SafeStateProfile、订阅规则禁止任意 C# / 脚本执行;周期能力与协议/看门狗匹配
仲裁与恢复TTL、RenewInterval、WaitTimeout、MaxTaskTime、HandoverTimeout、RecoveryProfileRenew ≤ TTL/3;运行超时不能直接触发新 owner;安全配置需审核
发布预览变更差异、受影响穴位/Run、Hash、验证报告、审批人运行中影响连接/输出/映射的变更禁止直接 Apply

5.1 批量生成 120 穴位

  1. 选择拓扑模板:“120×1 TCP”或“15×8 多通道”;设置地址规则、穴位范围和设备编号前缀。
  2. 生成草稿和映射预览,显示全部 120 条映射;单点覆盖保留来源与差异。
  3. 离线校验身份冲突、能力、限值、Broker 域和驱动依赖;通过后申请维护窗口执行连接探测、通道寻址和读回。
  4. 发布不可变 ConfigVersion;分发到各宿主 Prepare 并确认同一 Hash。
  5. 停止受影响 Run,撤销写权限,安全断连;各宿主 Apply 并自检。全部成功后才更新 Effective 指针;失败保持停用并按上一版配置恢复。
  6. 创建新 Run 时复制生效快照;既有 Run 继续引用原 Hash,不允许静默切版。

5.2 配置状态与失败处理

Draft → Validated → Published → Prepared → Effective → Retired。Published 内容不可修改,回滚也建立新的生效记录。多 Broker 生效采用停机窗口中的两阶段分发和最终确认;它不是跨机器数据库事务。若部分宿主 Apply 成功、部分失败,整组标记 ApplyFailed,阻止 Run 启动,直至所有宿主统一到旧版或新版并完成读回。

06 配置契约与启动校验

JSON / 配置示例
{
  "schemaVersion": 3,
  "configVersion": "CFG-20261103-001",
  "topology": "15x8", "slotCount": 120,
  "authorities": [
    {"id":"broker-b01", "host":"IPC-01", "pipe":"aging.b01.v3"},
    {"id":"local-ipc01", "host":"IPC-01", "db":"arb.db"}
  ],
  "devices": [{
    "deviceId":"PSU01", "executionMode":"Broker",
    "authorityId":"broker-b01", "sessionKey":"psu01/main",
    "physicalIdentity":"serial:TBD-PSU01",
    "driver":{"type":"MultiChannelScpi", "version":"TBD", "sha256":"TBD"},
    "endpointRef":"endpoint/psu01", "channels":8,
    "accessPolicy":"ExclusiveChannelWithParentPermit",
    "safeStateProfile":"PSU-SAFE-001"
  }],
  "bindings": [{"slotId":"S001", "capability":"Power",
    "deviceId":"PSU01", "channelId":1, "resourceKey":"psu/psu01/ch/1"}],
  "leasePolicy":{"ttlMs":30000,"renewMs":8000,"handoverMs":30000},
  "taskPolicy":{"maxQueuedPerSession":128,"defaultDeadlineMs":5000},
  "note":"字段示例,仅列一个设备和一个穴位;完整120映射见第17章。"
}

JSON 采用固定序列化规则(UTF-8、稳定字段/数组排序、排除自身 Hash 字段)计算 SHA256。Secrets、运行状态、当前租约不得进入可迁移明文配置。Schema 校验和语义校验分别输出字段路径、错误码、影响穴位与建议处理。

启动顺序:读取有效版本 → 校验 manifest/包 Hash 与驱动位数 → 检查 Authority 唯一实例和 epoch → 打开数据库并迁移 → 加载设备身份与连接守卫 → 进入 Recovering → 执行无输出连接探测/状态读回 → 标记设备 Ready → 发布 HostReady。Run 启动时要求所有绑定资源 Ready,且宿主回报的配置 Hash 与 RunSnapshot 完全一致。示例中的 TBD 不允许通过生产发布门禁。

07 数据库边界、实体与一致性

库唯一写入责任主要内容一致性边界
aging_system.db / config.dbConfigurationService / RunService不可变配置、Run 快照、版本索引、用户/审计、结果归档索引配置发布与 Run 起始记录需事务;采样不混入配置库
arb.db(同机 Local)受控 Local Lease Store;多进程短事务排他资源状态、父 Permit、进程身份、租约历史仅同一主机本地磁盘;PhysicalSessionKey Mutex 另行守卫
broker-b01.db 等各 Broker 单一写入器本域 fence、任务日志、事件、结果 Outbox、恢复检查点每个 Broker 一个 Authority;其他进程只走 IPC
每 Run aging_data.dbResultWriter样本、信号、步骤结果、质量和时间戳批量写入,独立背压;通过 RunId / DUTExecutionId 关联

SQLite 使用本机磁盘;禁止放在 SMB/NAS 上让多机同时写 WAL。所有连接显式设置 foreign_keys、busy_timeout;WAL 只允许一个同时写事务,必须缩短事务并避免在事务内执行 I/O。启用 WAL 不等于任意写并发,也不等于无限吞吐。跨机扩展经 Broker 服务通信,不能共享同一个 arb.db 文件。[E1][E2]

关联关系:ConfigSnapshot 1:N Device / Resource / Binding / TaskTemplate;RunSnapshot 引用 Config Hash;ResourceState 1:N LeaseHistory;BrokerTask 引用 Config Hash 并在请求 JSON 中携带完整 grant 集;Task 1:N Event,Task 1:1 ResultOutbox。历史日志保留资源名和快照 Hash,不能被修改后的当前配置反向解释。

UTC 毫秒用于审计,宿主的单调时钟用于活跃租约和 deadline 判定。epoch + boot identity 防止重启后沿用旧单调时钟;本地系统时间跳变时停止续租/交接并进入恢复检查。SQLite datetime('now') 不是独立可信时钟。

08 SQLite DDL 与租约事务

以下两段 DDL 分别用于配置库和每个仲裁域的运行库。它们是新增 V3 schema,不直接对现有 data.db 执行 DROP/覆盖。迁移程序维护 schema_version、备份和回滚标记;生产迁移必须在停机窗口完成。

SQL · 配置库
PRAGMA foreign_keys=ON;
PRAGMA journal_mode=WAL;
PRAGMA synchronous=FULL;
PRAGMA busy_timeout=3000;
CREATE TABLE config_snapshot (
  config_hash TEXT PRIMARY KEY, version_id TEXT NOT NULL UNIQUE,
  schema_version INTEGER NOT NULL CHECK(schema_version=3),
  content_json TEXT NOT NULL, author TEXT NOT NULL,
  published_utc_ms INTEGER NOT NULL
);
CREATE TABLE effective_config (
  scope_id TEXT PRIMARY KEY, config_hash TEXT NOT NULL
    REFERENCES config_snapshot(config_hash), revision INTEGER NOT NULL
);
CREATE TABLE resource_definition (
  config_hash TEXT NOT NULL REFERENCES config_snapshot(config_hash),
  resource_key TEXT NOT NULL, device_id TEXT NOT NULL, channel_id INTEGER NOT NULL,
  parent_key TEXT, session_key TEXT NOT NULL, physical_identity TEXT NOT NULL,
  execution_mode TEXT NOT NULL CHECK(execution_mode IN ('Local','Broker')),
  authority_id TEXT NOT NULL, owner_process_id TEXT,
  access_policy TEXT NOT NULL, safe_profile TEXT NOT NULL,
  PRIMARY KEY(config_hash, resource_key)
);
CREATE TABLE slot_binding (
  config_hash TEXT NOT NULL, slot_id TEXT NOT NULL, capability TEXT NOT NULL,
  resource_key TEXT NOT NULL, group_id TEXT NOT NULL,
  PRIMARY KEY(config_hash, slot_id, capability),
  FOREIGN KEY(config_hash, resource_key)
    REFERENCES resource_definition(config_hash, resource_key)
);
CREATE INDEX ix_binding_resource ON slot_binding(config_hash, resource_key);
CREATE TABLE broker_task_template (
  config_hash TEXT NOT NULL REFERENCES config_snapshot(config_hash),
  template_id TEXT NOT NULL, template_version TEXT NOT NULL,
  operation_code TEXT NOT NULL, parameters_schema TEXT NOT NULL,
  required_resources_json TEXT NOT NULL, max_duration_ms INTEGER NOT NULL,
  safe_profile TEXT NOT NULL,
  PRIMARY KEY(config_hash,template_id,template_version)
);
CREATE TABLE run_snapshot (
  run_id TEXT PRIMARY KEY, config_hash TEXT NOT NULL
    REFERENCES config_snapshot(config_hash), recipe_hash TEXT NOT NULL,
  driver_manifest_hash TEXT NOT NULL, dbc_hash TEXT NOT NULL,
  uds_hash TEXT NOT NULL, slot_set_json TEXT NOT NULL, started_utc_ms INTEGER NOT NULL
);
CREATE TABLE config_apply_ack (
  apply_id TEXT NOT NULL, authority_id TEXT NOT NULL,
  config_hash TEXT NOT NULL REFERENCES config_snapshot(config_hash),
  phase TEXT NOT NULL, readback_json TEXT NOT NULL, utc_ms INTEGER NOT NULL,
  PRIMARY KEY(apply_id,authority_id)
);
SQL · 运行库
PRAGMA foreign_keys=ON;
PRAGMA journal_mode=WAL;
PRAGMA synchronous=FULL;
PRAGMA busy_timeout=3000;
CREATE TABLE authority_meta (
  authority_id TEXT PRIMARY KEY, epoch INTEGER NOT NULL,
  boot_id TEXT NOT NULL, schema_version INTEGER NOT NULL
);
CREATE TABLE resource_state (
  resource_key TEXT PRIMARY KEY, generation INTEGER NOT NULL DEFAULT 0,
  phase TEXT NOT NULL CHECK(phase IN ('Free','Active','Draining','RecoveryRequired','Disabled')),
  lease_id TEXT UNIQUE, owner_id TEXT, owner_type TEXT, group_id TEXT,
  process_instance_id TEXT, epoch INTEGER NOT NULL,
  expires_utc_ms INTEGER, handover_utc_ms INTEGER, updated_utc_ms INTEGER NOT NULL,
  CHECK(phase <> 'Free' OR lease_id IS NULL)
);
CREATE INDEX ix_resource_expiry ON resource_state(phase,expires_utc_ms);
CREATE TABLE parent_permit (
  parent_key TEXT NOT NULL, lease_id TEXT NOT NULL,
  mode TEXT NOT NULL CHECK(mode IN ('Shared','Exclusive')),
  owner_id TEXT NOT NULL, epoch INTEGER NOT NULL,
  expires_utc_ms INTEGER NOT NULL,
  PRIMARY KEY(parent_key,lease_id)
);
CREATE TABLE lease_history (
  event_id INTEGER PRIMARY KEY AUTOINCREMENT, resource_key TEXT NOT NULL,
  generation INTEGER NOT NULL, epoch INTEGER NOT NULL, lease_id TEXT,
  owner_id TEXT, event_type TEXT NOT NULL, reason TEXT NOT NULL, utc_ms INTEGER NOT NULL
);
CREATE TABLE broker_task (
  task_id TEXT PRIMARY KEY, client_instance_id TEXT NOT NULL,
  idempotency_key TEXT NOT NULL, payload_sha256 TEXT NOT NULL,
  config_hash TEXT NOT NULL, epoch INTEGER NOT NULL,
  template_id TEXT NOT NULL, request_json TEXT NOT NULL,
  state TEXT NOT NULL CHECK(state IN ('Accepted','Queued','Running','Succeeded',
    'Failed','Cancelled','Expired','Unknown','RecoveryRequired')),
  priority INTEGER NOT NULL, submitted_utc_ms INTEGER NOT NULL,
  deadline_utc_ms INTEGER NOT NULL, started_utc_ms INTEGER, finished_utc_ms INTEGER,
  result_json TEXT, error_code TEXT,
  UNIQUE(client_instance_id,idempotency_key)
);
CREATE INDEX ix_task_queue ON broker_task(state,priority,submitted_utc_ms);
CREATE TABLE broker_event (
  cursor INTEGER PRIMARY KEY AUTOINCREMENT, task_id TEXT,
  event_type TEXT NOT NULL, payload_json TEXT NOT NULL, utc_ms INTEGER NOT NULL
);
CREATE TABLE result_outbox (
  result_id TEXT PRIMARY KEY, task_id TEXT NOT NULL UNIQUE REFERENCES broker_task(task_id),
  payload_json TEXT NOT NULL, state TEXT NOT NULL CHECK(state IN ('Pending','Acked')),
  attempt INTEGER NOT NULL DEFAULT 0, created_utc_ms INTEGER NOT NULL
);
CREATE TABLE recovery_checkpoint (
  resource_key TEXT PRIMARY KEY, epoch INTEGER NOT NULL, status TEXT NOT NULL,
  physical_readback_json TEXT NOT NULL, approved_by TEXT,
  evidence_json TEXT NOT NULL, updated_utc_ms INTEGER NOT NULL
);
CREATE TABLE operation_audit (
  audit_id INTEGER PRIMARY KEY AUTOINCREMENT, correlation_id TEXT NOT NULL,
  principal_id TEXT NOT NULL, owner_id TEXT NOT NULL, task_id TEXT,
  resource_key TEXT NOT NULL, action TEXT NOT NULL, outcome TEXT NOT NULL,
  detail_json TEXT NOT NULL, utc_ms INTEGER NOT NULL
);
SQL + 事务编排说明
-- 前提:请求资源已展开、规范化、去重、Ordinal排序,属于同一 Authority。
-- 每个叶资源必须 Free;Expired 不属于 Free。父 Permit 冲突也在本事务检查。
BEGIN IMMEDIATE;
-- 应用先 SELECT 并检查全部叶节点 + parent_permit,无跨域/缺项/冲突才继续。
SELECT resource_key, generation, phase, epoch FROM resource_state
 WHERE resource_key IN (@r1, @r2);
-- 对每个叶资源执行下列参数化 UPDATE,并检查 changes() = 1。
UPDATE resource_state
 SET generation=generation+1, phase='Active', lease_id=@lease_id,
     owner_id=@owner_id, owner_type=@owner_type, group_id=@group_id,
     process_instance_id=@process_id, epoch=@epoch,
     expires_utc_ms=@expires, updated_utc_ms=@now
 WHERE resource_key=@resource_key AND phase='Free' AND epoch=@epoch;
-- 父Permit冲突检查后插入;只要任一条失败,整个事务 ROLLBACK。
INSERT INTO parent_permit(parent_key,lease_id,mode,owner_id,epoch,expires_utc_ms)
 VALUES(@parent,@permit_id,'Shared',@owner_id,@epoch,@expires);
-- 同事务插入 lease_history,COMMIT 成功后才返回 Grant。
COMMIT;

-- Renew:保持 Generation 不变;禁止过期租约复活。
UPDATE resource_state SET expires_utc_ms=@new_expires, updated_utc_ms=@now
 WHERE resource_key=@resource_key AND lease_id=@lease_id
   AND generation=@generation AND epoch=@epoch
   AND phase='Active' AND expires_utc_ms > @now;
-- 父Permit同事务续期;组内任一项失败 => 回滚续期、组整体失效并隔离。
-- Draining 续期走专门分支,上限不得超过 handover_utc_ms。

-- TTL过期:保留Generation和owner证据,进入恢复,不直接清空或删行。
UPDATE resource_state SET phase='RecoveryRequired', updated_utc_ms=@now
 WHERE phase IN ('Active','Draining') AND expires_utc_ms <= @now;
-- 只有恢复协调器读回合格、旧I/O已隔离、父Permit正确归还后可变为Free。
-- 正常Release亦须在SessionGate内完成排空及任务清理后再置Free。

初始化时为已发布资源创建 Free 行,generation=0;删除设备也保留 fence 墓碑。生成号永不因释放、重启或清理历史回退。恢复旧数据库备份后必须更换 Authority identity/epoch,禁止旧 token 再次有效。SELECT 结果的分支与 changes() 检查由 C# 在同一连接事务内实现,不使用 SQLite 不支持的 SELECT INTO / FOR UPDATE。[E1]

持续数据、任务终态、Outbox 与 Event 在同一运行库事务提交;ACK 仅在消费者持久化成功后发送。Outbox 保留到 ACK,消费者按 ResultId 唯一约束去重。日志清理不得删除尚未 ACK、Unknown、RecoveryRequired 或被 Run 引用的证据。

09 C# 接口与兼容边界

以下为 V3.0 合约基线,可抽出为独立类库。使用 Task 和普通类以适配当前 net472,不依赖 TimeProvider、PeriodicTimer、record 或原生 await using。序列化工具应显式映射只读 LeaseGrant 构造器。Token 是授权引用,服务端还必须校验连接身份与 owner;不能把可见 token 当作签名凭据。

C# · 合约
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace Aging.ResourceContracts.V3
{
    public enum ExecutionMode { Local, Broker }
    public enum OwnerKind { Production, Maintenance, SystemSafety }
    public enum TaskState { Accepted, Queued, Running, Succeeded, Failed,
        Cancelled, Expired, Unknown, RecoveryRequired }

    // Broker/Store签发;必须按持久化字段还原,不能在反序列化时生成新LeaseId。
    public sealed class LeaseGrant
    {
        public string AuthorityId { get; }
        public string ResourceKey { get; }
        public string LeaseId { get; }
        public string OwnerId { get; }
        public long Epoch { get; }
        public long Generation { get; }
        public long ExpiresUtcMs { get; }
        public LeaseGrant(string authorityId, string resourceKey, string leaseId,
            string ownerId, long epoch, long generation, long expiresUtcMs)
        {
            AuthorityId=authorityId; ResourceKey=resourceKey; LeaseId=leaseId;
            OwnerId=ownerId; Epoch=epoch; Generation=generation;
            ExpiresUtcMs=expiresUtcMs;
        }
    }
    public sealed class AcquireRequest
    {
        public string OwnerId { get; set; } = "";
        public OwnerKind OwnerKind { get; set; }
        public string ProcessInstanceId { get; set; } = "";
        public string ConfigHash { get; set; } = "";
        public string[] ResourceKeys { get; set; } = new string[0];
        public int WaitTimeoutMs { get; set; }
    }
    public interface ILeaseScope
    {
        IReadOnlyList<LeaseGrant> Grants { get; }
        CancellationToken LeaseLost { get; }
        Task ReleaseAsync(CancellationToken cancellationToken);
    }
    public interface IResourceCoordinator
    {
        Task<ILeaseScope> AcquireGroupAsync(AcquireRequest request, CancellationToken ct);
        Task<IReadOnlyList<LeaseGrant>> RenewGroupAsync(
            IReadOnlyList<LeaseGrant> grants, CancellationToken ct);
        Task RequestHandoverAsync(string resourceKey, string reason, CancellationToken ct);
    }
    public sealed class DeviceOperation
    {
        public string OperationCode { get; set; } = "";
        public string ParametersJson { get; set; } = "{}";
        public string IdempotencyKey { get; set; } = "";
        public string ConfigHash { get; set; } = "";
        public IReadOnlyList<LeaseGrant> Grants { get; set; } = new LeaseGrant[0];
        public long DeadlineUtcMs { get; set; }
    }
    public sealed class OperationResult
    {
        public string RequestId { get; set; } = "";
        public TaskState State { get; set; }
        public string ErrorCode { get; set; } = "";
        public string ReadbackJson { get; set; } = "{}";
        public bool PhysicalStateKnown { get; set; }
    }
    public interface IDeviceExecutor
    {
        Task<OperationResult> ExecuteAsync(DeviceOperation op, CancellationToken ct);
    }
    public sealed class BrokerTaskRequest
    {
        public string ClientInstanceId { get; set; } = "";
        public string TemplateId { get; set; } = "";
        public string TemplateVersion { get; set; } = "";
        public DeviceOperation Operation { get; set; } = new DeviceOperation();
    }
    public sealed class BrokerTaskTicket
    {
        public string TaskId { get; set; } = "";
        public TaskState State { get; set; }
    }
    public interface IDeviceBrokerClient
    {
        Task<BrokerTaskTicket> SubmitAsync(BrokerTaskRequest request, CancellationToken ct);
        Task<OperationResult> QueryAsync(string taskId, CancellationToken ct);
        Task CancelAsync(string taskId, string reason, CancellationToken ct);
        Task AckResultAsync(string resultId, CancellationToken ct);
    }
}
现有接口/类V3.0 迁移方式影响
IResourceManager保留旧单资源方法为过渡 Adapter;新增 IResourceCoordinator续期、组预约与异步状态查询为新接口,业务调用需适配
ResourceOwnerToken仅保留业务 owner 元数据;I/O 使用每资源独立 LeaseGrant一个 owner 可持有多个不同 generation,禁止共用可变 Generation
IAutoDeviceFacade / IAutoDeviceLeaseLeaseScope 注入心跳与 LeaseLost;显式异步 ReleaseDispose 不得阻塞 UI;释放失败应可查询和进入恢复
DeviceService通过 SnapshotResolver + ResourceRouter 调用 IDeviceExecutor已有 raw command 仅留受控维护入口,生产用类型化 OpCode
DriverFactory / DeviceManager区分宿主驱动与 BrokerProxy;Proxy 不加载设备 SDK位数冲突的 SDK 独立 Broker 进程,IPC 合约保持一致
DebugSessionManager保存 session、grants、handover 与恢复结果不能重新构造 owner token 代替恢复持久化 token;监控与写权限分开

10 执行入口、生命周期与取消语义

10.1 单次操作

  1. 步骤解析 Slot/Capability,得到冻结版本下的资源集合与宿主;检查 Run / DUTExecution 状态。
  2. 申请组预约(或复用已有 Scope),取得叶节点和父 Permit;启动独立续期任务。
  3. DeviceService 校验动作、参数、限值和软件联锁,构造 OperationCode 与 IdempotencyKey。
  4. LocalExecutor 或 Broker 在入队时校验;出队并持有 SessionGate 后再次校验完整 grant、epoch、ConfigHash、deadline 与连接身份。
  5. 执行不可拆分的选择通道/命令/响应片段;读回核对并记录质量。联锁故障打断可中断任务,已发动作按硬件能力处理。
  6. 提交 Task 终态、事件和 Outbox;结果回到步骤引擎,由引擎判定步骤/产品结果。
  7. Scope 退出先停止续期和后台任务,排空 I/O,再读回/安全交接并释放。失败进入 RecoveryRequired,绝不仅记录日志后假装可用。

10.2 伪代码:SessionGate 与有效期检查

C# · 执行算法
// 执行算法示意,依赖的Store/Driver不是本文已实现代码。
using (await sessionGate.EnterAsync(sessionKey, ct))
{
    await authority.ValidateAtDispatchAsync(op.Grants, principal, ct);
    ValidateConfigAndDeadline(op);
    await safety.CheckAsync(op, ct);
    // 切换通道 + 写 + 读响应保持在同一Gate,禁止中途释放。
    await journal.MarkRunningAsync(requestId, ct);  // I/O之前持久化
    var result = await driver.ExecuteAtomicAsync(op, ioToken);
    await journal.CommitResultAndOutboxAsync(requestId, result, persistToken);
}
// 每个不可逆子动作前再次核对lease;续期不能抢同一被长任务占用的调度队列。
// 交接先关闭新入队、取消待执行任务、等待当前原子片段终止并读回;
// 未能确认终止时保持隔离,禁止仅因Generation增加就启用新owner。

取消分三类:排队取消可确定未发送;执行中取消需驱动确认安全停止;响应等待取消仅停止等待,不能证明物理操作撤销。调用方的 CancellationToken 不得取消终态/审计持久化;终态保存使用有界的独立 token,失败则隔离。

不能在同一个非线程安全 SDK 会话中并发发送 SafeShutdown 与正常命令。安全通道必须有经验证的独立能力(如 PLC 硬线、独立控制口),否则等待会话退出,超时触发硬件策略并保持隔离。

11 Broker Task 模型、模板与状态机

Broker Task 是宿主执行的有界设备动作或可续租设备任务;它不是 C# Task 对象的序列化,也不是整个 DUT Recipe。Recipe 中的长 Delay 留在执行引擎;周期 CAN 发送、TesterPresent、采集订阅等可驻留 Broker,受 owner 租约和宿主看门狗约束。

Task 类型输入/资源完成条件失效策略
OneShot / AtomicSequence类型化命令 + grant 集 + deadline写入及必要读回完成超时按发送边界区分 Expired / Unknown
PeriodicTxCAN 帧模板、bus、周期、持续期限、ownerStop/到期/租约失效并确认停止周期错失跳过过时帧;禁止重连后追补旧帧
ProtocolSessionUDS 地址、会话参数、总期限、独占键响应完成或显式退出TesterPresent 与业务动作统一会话调度;异常恢复协议状态
Acquisition采样通道、周期、缓存策略、订阅 ID取消并确认停采/解绑允许数据 Gap,质量标记 Stale/Disconnected;不可伪造连续数据
SafeState受审安全模板及恢复授权物理读回符合模板失败维持 RecoveryRequired 并报警

11.1 任务字段与状态

TaskId、ClientInstanceId、IdempotencyKey、TemplateId/Version、PayloadSHA256、ConfigHash、RunId、DUTExecutionId、StepExecutionId、OwnerId、GrantSet、Priority、SubmittedAt、StartDeadline、MaxDuration、Attempt、State、Readback、ErrorCode、ResultId、CorrelationId。超时分为排队起始期限和运行上限,不能混用。

Accepted → Queued → Running → Succeeded / Failed / Cancelled / Unknown / RecoveryRequired;未启动任务可转 Expired,取消待执行任务可转 Cancelled。Accepted 必须已持久化,但不代表设备已执行。Running 落盘后才发 I/O;若崩溃在落盘与发送之间,恢复时仍按可能已发送处理,不能推测为未发送。

11.2 幂等与不可逆动作

同一 (ClientInstanceId, IdempotencyKey) 且同一 PayloadHash 返回已有 Task;同键不同内容报 IDEMPOTENCY_CONFLICT。客户端重连保留原 ClientInstanceId 查询,不生成新键重发。跨重启保留 Journal 与 epoch;旧 epoch 未开始任务全部失效,需新租约和新任务。命令执行与数据库提交之间无硬件事务,因此不承诺 exactly-once。SetVoltage 等动作能否重试必须按仪器行为和读回定义;触发、复位、烧写、计数动作默认禁止自动重放。

11.3 示例模板

JSON · 模板示例
{
 "templateId":"psu.set-readback", "version":"1",
 "allowedOperationCodes":["SelectChannel","SetVoltage","ReadVoltage"],
 "resources":["psu/{device}/ch/{channel}","parent-permit:{device}"],
 "atomicSession":"{device}/main", "maxDurationMs":3000,
 "validation":{"voltage":"within channel and recipe limit"},
 "onUnknown":"IsolateAndReadback", "retryClass":"ReconcileFirst"
}

模板允许参数替换,不能指定任意驱动类型、地址、脚本或文件路径。模板变更参与配置 Hash。PeriodicTx 的周期/抖动验收必须由真实 SDK 和设备测量,Windows 普通 Task/Timer 不构成硬实时保证。

12 Broker 调度、队列与遥测

运行单元调度规则背压与限制
IPC Accept / Auth协议握手、用户/进程身份验证、消息边界和长度限制连接数、消息大小、调用频率限额;默认 1 MiB 消息上限,待压测
Control WorkerAcquire/Renew/Release/Cancel/Query;与设备耗时 I/O 分离保留控制带宽;限流防普通请求耗尽心跳处理
Session Actor每真实连接单队列;每次仅一段原子 I/O队列上限示例128;满返回 QUEUE_FULL;过期任务不发送
Task Scheduler安全事件优先、维护让渡协调、生产按 owner 轮转同优先级公平;连续占用时间上限;不在任意命令中间抢占
Protocol WorkerCAN 单接收循环 + 解码/订阅;UDS 分会话调度按 SDK 线程安全能力分组;bus reset 属全局动作
Journal / Outbox Writer单写入器批量提交;关键状态先持久化不可丢写操作审计;磁盘满停止新增动作并隔离
Telemetry Fanout单次采集多订阅;携带来源、质量、序列号、时间UI 最新值可覆盖;生产样本使用有界队列,Gap 必须记录
Watchdog / Recovery独立心跳检查、异常隔离、恢复门禁不能因为忙而无限续租;不得循环快速重启

Session Actor 释放会话锁后等待稳定延时还是保持锁,取决于设备耦合:共享 DMM/矩阵须保持资源权利及路由不变;仪器可独立定时输出的电源任务可释放操作锁,但保留通道预约。队列串行化本身不意味着允许多个 Run 同时改变同一温箱设定值,温箱控制权必须绑定兼容的 Run/group。

硬件 Safety 与普通调度独立。示例公平策略为每 owner 一次原子片段后轮转;优先级只决定下一片段,不绕过租约或联锁。持续采集/周期发送占用逻辑权利但不永久占用普通调度线程。定时线程数量由会话/SDK 能力确定,不能按 120 穴位创建 120 轮询线程。

13 IPC 协议、身份与事件恢复

同机基线使用 Windows Named Pipe;服务名含 Station/Broker/协议主版本。现有 PipeClient 必须先修复响应不可达、serverName、取消和等待问题,再作为候选基础;不能直接宣称现有 SendMessage 为可靠请求-响应。[E3]

JSON · 消息外壳
{
 "protocolVersion":3, "messageType":"SubmitTask", "requestId":"req-...",
 "correlationId":"run-.../step-...", "clientInstanceId":"worker-01/boot-...",
 "authorityId":"broker-b01", "expectedEpoch":42,
 "configHash":"sha256:...", "deadlineUtcMs":1793673605000,
 "body":{"templateId":"psu.set-readback", "templateVersion":"1",
         "idempotencyKey":"run/dut/step/attempt-1", "grants":[], "parameters":{}}
}

Body 这里只是外壳示例;真实设备 Submit 必须含完整 grant 集,空数组被拒绝。应答回传 RequestId、TaskId、BrokerEpoch、State、ErrorCode、RetryAdvice、ServerUtcMs;对不兼容协议拒绝连接,不自动降级安全字段。

帧协议选择“4 字节长度 + UTF-8 JSON”并实现精确读取、最大帧限制、半包、粘包、响应关联与取消;即便使用 Message 模式,也要处理 IsMessageComplete 和大消息。每条连接单写入泵、单读取分发器,多个请求通过 RequestId 关联,禁止多个调用方同时直接读同一 stream。

管道 ACL 限制指定服务身份和工程账户;服务端从 OS 身份映射 Principal,不相信 body 中的角色或 OwnerKind。OwnerId 必须绑定已认证的 Run/DebugSession/进程实例。跨机扩展采用 TLS 的独立服务端口、双向身份验证和网络白名单,Named Pipe 实现不会自动变成安全的跨机网关。

事件带持久化单调 cursor。客户端断线后先 Query 未终态 Task,再按最后 cursor 续订;若 cursor 已被保留策略清除,返回 CURSOR_GONE,客户端取全量任务/资源快照并重建订阅。进程 PID 加 ProcessStartTime/随机 ProcessInstanceId,避免 PID 复用被误认为原持有者。

14 资源仲裁与维护交接

图2 V3.0目标架构设计
图 2 租约交接与异常恢复;超时进入隔离,不直接转为可用。
当前状态请求决策与下一步
Free 且 Ready生产/维护申请同域原子授予; generation+1;发放组 grant
Production Active另一个生产请求同通道有界排队或 RESOURCE_BUSY;不共享同一输出权
Production Active维护申请默认拒绝并显示 Run/穴位;授权让渡只在安全边界启动 Draining
Maintenance Active生产启动拒绝启动,显示维护会话及影响范围
共享读订阅维护写请求先停止相关写权;被动读可保留,必要时标记 Stale
Draining现 owner 续期仅续到 handover deadline;禁止新普通任务,允许收尾/安全动作
Draining/Active 过期新 owner 请求RecoveryRequired;等待排空、连接守卫与物理读回
RecoveryRequired / Disabled任何普通写请求拒绝;只有受控恢复动作允许执行
所有状态硬件急停/严重联锁硬件执行停机;软件阻断、记录状态并核查,不等待租约到期

14.1 预约、操作锁与读订阅

生产预约可覆盖一个 Run/步骤组,而 SessionGate 只覆盖一次原子 I/O。只读监控优先使用缓存;带副作用的 query、读清零寄存器、切换通道读取仍是排他 I/O,不能归为无锁读取。共享写只允许经明确的组合策略,例如 CAN 周期帧按 ID/周期规则分配,不能泛化为“同设备命令排队即可安全共享”。

14.2 完整维护交接

  1. 显示资源的占用者、Run、DUT、任务、剩余租约和父子影响;维护工程师填写理由。
  2. Run 协调器批准受影响穴位安全暂停,资源变 Draining;拒绝新写任务并停止周期任务。
  3. 等待在途片段结束;执行适用的 SafeStateProfile 并读回。超时转 RecoveryRequired,不自动给维护者。
  4. 原 owner 排空并释放;Local 还须确认旧句柄关闭和命名 Mutex 可获取。
  5. 确认独立联锁/断能手续后授予维护 Scope;维护动作全量审计。
  6. 退出维护先读回/复核、恢复通信配置和报警基线,再释放并由 RunService 明确恢复或新建 Run。

“Force release” 按钮不能直接 UPDATE/DELETE 租约行;它只能触发隔离及恢复流程。多人作业、拔线、接线和柜门操作的许可不由软件 token 单独判定。

15 Fencing、超时与 Local 边界

完整校验集合为 AuthorityId + Epoch + ResourceKey + LeaseId + Generation + OwnerId + ConfigHash + Principal,并检查活跃期、parent permit、deadline 与设备 Ready。generation 对每个资源单调增加;续期保持不变。Broker 在 SessionGate 内处理交接与最终发命令的序列,旧任务不能排队越过新 owner 的授权边界。

15.1 已发命令无法被 token 撤销

进程暂停可能发生在最后一次校验之后、写入之前;因此只有查询数据库再直连设备,不能提供强物理 fencing。Broker 在同一 I/O 域内以执行串行和交接排空避免旧/新 owner 并行;对已发出的命令,仍需等待完成、设备取消能力或独立断能。若驱动无法终止且状态未知,不授予新租约。

15.2 Local 的准入限制

Local 会话由配置固定到唯一 Worker,连接守卫使用规范化 PhysicalSessionKey 的同机命名 Mutex。获取守卫的专用宿主线程持续持有至连接关闭(Windows Mutex 为线程所有,不能简单跨 await 线程释放);驱动操作另用 SemaphoreSlim/SessionGate。线程异常与 AbandonedMutex 统一转恢复检查。Mutex 不因 TTL 过期释放,暂停的旧进程仍持有它,因此新进程不得接管。

远程客户端、另一个软件或同设备的其他别名不受此同机 Mutex 控制。若设备不能限制外部直连,且需要跨进程自动切换/共享,必须 Broker 化并配网络 ACL/访问凭据或硬件连接隔离;不能在文档中承诺绝对阻止所有外部命令。工程师维护 Local 设备时优先经现有宿主代理;若必须迁移连接,先停止旧 Worker 并确认无旧句柄,再走恢复门禁。

15.3 时间参数

建议初始 TTL=30 s、续期=8 s、让渡上限=30 s,均为设计默认值。权威宿主以单调时钟判定;UTC 仅用于持久化/展示,外来 UTC deadline 转换为有上限的本地预算。心跳失败后立即阻断新操作;没有有效确认不能无限延长本地有效期。系统重启或宿主 epoch 变化后全部旧 grant 失效,待核对后重新预约。

16 异常恢复与重启编排

故障即时动作恢复判据 / 禁止事项
Worker 崩溃或断线停其周期发送/订阅写任务;本域租约到期转 RecoveryRequired核对在途任务与输出;进程死亡不等于设备输出关闭
Broker 崩溃受影响 DUT 标记设备不可用;Supervisor 限频拉起持久化 epoch+1;Running→Unknown;旧队列失效;读回后 Ready,禁止重放旧命令
宿主暂停/卡死心跳超时、联锁策略介入;新 owner 不抢占确认旧 I/O 宿主退出或隔离、硬件状态已知
TCP/串口断线、USB重枚举Session Faulted;不缓存并追发旧写命令重新识别设备序列号、通道/配置、输出状态;更换设备需配置审批
超时/回包丢失区分未发送与可能已执行;后者 Unknown按模板读回;无法判定则人工恢复,不按 RetryAdvice 盲发
DB busy / disk full / 损坏限制短重试;停止接受新写动作;关键审计失败即隔离DB 完整性检查、空间恢复、Outbox 补投;不能绕开 Journal 继续生产
系统断电/重启由硬件保障断能策略;启动进入 Recovering旧 epoch失效;核对SN/穴位、物理输出和温箱;创建恢复记录
配置Hash不一致拒绝启动和操作对齐宿主版本、驱动包与映射,重新 Prepare/Apply
CAN bus-off/UDS会话失效隔离总线/会话;停止相关周期任务,标记数据质量有界重连、地址/会话重新建立;不能把协议会话当成连续有效
安全联锁动作硬件执行;软件停止新任务并记录原因/受影响资源人工复位并读回,生产恢复必须显式批准
结果消费者断线Outbox 保留,限制积压并报警按ResultId补投,消费者落盘再ACK;不能吞掉失败结果

16.1 Broker 重启步骤

  1. 获取 BrokerId 单实例守卫;同一物理会话不允许双实例同时启动。
  2. 数据库短事务增加 epoch 并记录 boot identity;旧 Active/Draining→RecoveryRequired,旧 Running→Unknown,未开始旧任务→Expired。
  3. 阻断普通写请求,加载有效配置,验证设备身份,建立连接;读取输出/通道/协议状态。
  4. 按设备安全模板执行经审核的恢复动作。安全状态不是统一“所有输出 Off”:温箱、冷却与被测件有各自安全要求。
  5. 恢复检查点和证据持久化;若需人工审批则等待。全部通过后资源 Free/Ready,再通知 RunService。
  6. RunService 对每个 DUT 记录中断窗口与数据 Gap,决定继续、重新执行可重入步骤或终止重测。168 h 等长时老化如何计时须按产品策略冻结。

Supervisor 建议退避 1/2/5/10/30 s,5 分钟内重启超过 3 次锁定报警,数值为待验证默认。软件重启恢复仅表示通信和状态恢复,不表示测试结果自动合格。

17 120 穴位部署方案与完整映射

图3 V3.0目标架构设计
图 3 单 IPC 的 120 穴位基线部署:6 个执行 Worker、3 个 Broker 域;数量为设计建议。

基线选择单 IPC、15 台 8 通道 PSU、120 个输出通道。建议划分 3 个设备域,每域 40 穴位、5 台 PSU;6 个 Worker,每 Worker 20 个 DUTContext。一个 HMI、一个 ResultWriter 和一个 Supervisor 构成运维基线。Worker 与 Broker 数量由进程隔离和压测决定,不是硬件既定事实;CAN 卡/PLC/温箱按物理 SDK 与控制域分配,不能仅因 PSU 分成 3 域就复制连接。

域Worker / 穴位PSUBroker职责
B01W01:S001–S020;W02:S021–S040PSU01–PSU05;各8通道PSU共享会话;本域资源与任务
B02W03:S041–S060;W04:S061–S080PSU06–PSU10;各8通道同上,独立数据库和故障域
B03W05:S081–S100;W06:S101–S120PSU11–PSU15;各8通道同上,独立数据库和故障域
全局共享所有涉及同一温箱/PLC的Run温箱01/PLC01/共用CAN(实际TBD)固定唯一宿主;必要时增加B00;控制权按Run/group分配

17.1 映射公式与边界

对穴位 s∈[1,120]:PSU=⌊(s−1)/8⌋+1;CH=((s−1) mod 8)+1;Broker=⌊(PSU−1)/5⌋+1;Worker=⌊(s−1)/20⌋+1。例如 S001→PSU01/CH1,S008→PSU01/CH8,S009→PSU02/CH1,S120→PSU15/CH8。

W01/W02 等的 20 穴位边界会切分一台多通道 PSU(如 PSU03);这是选择 Broker 的实际原因之一。通道预约独立,整机命令等待父 Permit,SessionGate 保证选通道和响应不串线。以下展开全部 120 条输出映射,通信地址、序列号和通道额定值须由工程师填充后才可发布。

120 穴位映射表(完整)
穴位WorkerBroker设备通道ResourceKey
S001W01B01PSU011psu/psu01/ch/1
S002W01B01PSU012psu/psu01/ch/2
S003W01B01PSU013psu/psu01/ch/3
S004W01B01PSU014psu/psu01/ch/4
S005W01B01PSU015psu/psu01/ch/5
S006W01B01PSU016psu/psu01/ch/6
S007W01B01PSU017psu/psu01/ch/7
S008W01B01PSU018psu/psu01/ch/8
S009W01B01PSU021psu/psu02/ch/1
S010W01B01PSU022psu/psu02/ch/2
S011W01B01PSU023psu/psu02/ch/3
S012W01B01PSU024psu/psu02/ch/4
S013W01B01PSU025psu/psu02/ch/5
S014W01B01PSU026psu/psu02/ch/6
S015W01B01PSU027psu/psu02/ch/7
S016W01B01PSU028psu/psu02/ch/8
S017W01B01PSU031psu/psu03/ch/1
S018W01B01PSU032psu/psu03/ch/2
S019W01B01PSU033psu/psu03/ch/3
S020W01B01PSU034psu/psu03/ch/4
S021W02B01PSU035psu/psu03/ch/5
S022W02B01PSU036psu/psu03/ch/6
S023W02B01PSU037psu/psu03/ch/7
S024W02B01PSU038psu/psu03/ch/8
S025W02B01PSU041psu/psu04/ch/1
S026W02B01PSU042psu/psu04/ch/2
S027W02B01PSU043psu/psu04/ch/3
S028W02B01PSU044psu/psu04/ch/4
S029W02B01PSU045psu/psu04/ch/5
S030W02B01PSU046psu/psu04/ch/6
S031W02B01PSU047psu/psu04/ch/7
S032W02B01PSU048psu/psu04/ch/8
S033W02B01PSU051psu/psu05/ch/1
S034W02B01PSU052psu/psu05/ch/2
S035W02B01PSU053psu/psu05/ch/3
S036W02B01PSU054psu/psu05/ch/4
S037W02B01PSU055psu/psu05/ch/5
S038W02B01PSU056psu/psu05/ch/6
S039W02B01PSU057psu/psu05/ch/7
S040W02B01PSU058psu/psu05/ch/8
S041W03B02PSU061psu/psu06/ch/1
S042W03B02PSU062psu/psu06/ch/2
S043W03B02PSU063psu/psu06/ch/3
S044W03B02PSU064psu/psu06/ch/4
S045W03B02PSU065psu/psu06/ch/5
S046W03B02PSU066psu/psu06/ch/6
S047W03B02PSU067psu/psu06/ch/7
S048W03B02PSU068psu/psu06/ch/8
S049W03B02PSU071psu/psu07/ch/1
S050W03B02PSU072psu/psu07/ch/2
S051W03B02PSU073psu/psu07/ch/3
S052W03B02PSU074psu/psu07/ch/4
S053W03B02PSU075psu/psu07/ch/5
S054W03B02PSU076psu/psu07/ch/6
S055W03B02PSU077psu/psu07/ch/7
S056W03B02PSU078psu/psu07/ch/8
S057W03B02PSU081psu/psu08/ch/1
S058W03B02PSU082psu/psu08/ch/2
S059W03B02PSU083psu/psu08/ch/3
S060W03B02PSU084psu/psu08/ch/4
S061W04B02PSU085psu/psu08/ch/5
S062W04B02PSU086psu/psu08/ch/6
S063W04B02PSU087psu/psu08/ch/7
S064W04B02PSU088psu/psu08/ch/8
S065W04B02PSU091psu/psu09/ch/1
S066W04B02PSU092psu/psu09/ch/2
S067W04B02PSU093psu/psu09/ch/3
S068W04B02PSU094psu/psu09/ch/4
S069W04B02PSU095psu/psu09/ch/5
S070W04B02PSU096psu/psu09/ch/6
S071W04B02PSU097psu/psu09/ch/7
S072W04B02PSU098psu/psu09/ch/8
S073W04B02PSU101psu/psu10/ch/1
S074W04B02PSU102psu/psu10/ch/2
S075W04B02PSU103psu/psu10/ch/3
S076W04B02PSU104psu/psu10/ch/4
S077W04B02PSU105psu/psu10/ch/5
S078W04B02PSU106psu/psu10/ch/6
S079W04B02PSU107psu/psu10/ch/7
S080W04B02PSU108psu/psu10/ch/8
S081W05B03PSU111psu/psu11/ch/1
S082W05B03PSU112psu/psu11/ch/2
S083W05B03PSU113psu/psu11/ch/3
S084W05B03PSU114psu/psu11/ch/4
S085W05B03PSU115psu/psu11/ch/5
S086W05B03PSU116psu/psu11/ch/6
S087W05B03PSU117psu/psu11/ch/7
S088W05B03PSU118psu/psu11/ch/8
S089W05B03PSU121psu/psu12/ch/1
S090W05B03PSU122psu/psu12/ch/2
S091W05B03PSU123psu/psu12/ch/3
S092W05B03PSU124psu/psu12/ch/4
S093W05B03PSU125psu/psu12/ch/5
S094W05B03PSU126psu/psu12/ch/6
S095W05B03PSU127psu/psu12/ch/7
S096W05B03PSU128psu/psu12/ch/8
S097W05B03PSU131psu/psu13/ch/1
S098W05B03PSU132psu/psu13/ch/2
S099W05B03PSU133psu/psu13/ch/3
S100W05B03PSU134psu/psu13/ch/4
S101W06B03PSU135psu/psu13/ch/5
S102W06B03PSU136psu/psu13/ch/6
S103W06B03PSU137psu/psu13/ch/7
S104W06B03PSU138psu/psu13/ch/8
S105W06B03PSU141psu/psu14/ch/1
S106W06B03PSU142psu/psu14/ch/2
S107W06B03PSU143psu/psu14/ch/3
S108W06B03PSU144psu/psu14/ch/4
S109W06B03PSU145psu/psu14/ch/5
S110W06B03PSU146psu/psu14/ch/6
S111W06B03PSU147psu/psu14/ch/7
S112W06B03PSU148psu/psu14/ch/8
S113W06B03PSU151psu/psu15/ch/1
S114W06B03PSU152psu/psu15/ch/2
S115W06B03PSU153psu/psu15/ch/3
S116W06B03PSU154psu/psu15/ch/4
S117W06B03PSU155psu/psu15/ch/5
S118W06B03PSU156psu/psu15/ch/6
S119W06B03PSU157psu/psu15/ch/7
S120W06B03PSU158psu/psu15/ch/8

17.2 120×1 Local 替代方案

若实物为 120 台独立 TCP PSU,按 6 Worker×20 会话固定归属;每 Worker 持 20 个设备连接,但使用异步收发与有界并发,不建 20 条专用轮询线程。共享 CAN/PLC/温箱仍经 Broker;Local PSU 维护经所属 Worker 代理或停机交接。若仪器全局命令跨穴位、SDK 不允许多实例或需自动宿主切换,应转 Broker。两种拓扑由 ConfigVersion 明确选择,不能混用映射公式。

17.3 多机扩展

可将每 40 穴位域迁移到独立 IPC,通过 TLS Broker API 连接;数据库仍保存在各自主机。共享温箱只设一个控制宿主,断网策略由独立联锁执行。域迁移需停用、隔离旧宿主、更新 Authority/epoch、应用新配置并核对输出,不做在线双主切换。

18 容量估算、数据与性能预算

以下数字用于建立压测负载,均非现有软件或硬件实测。独立电压/电流样本与 CAN 信号数据需分别预算,不能把 UI 刷新频率当作采样频率。

项目计算/建议起点验证方式
PSU采样,10 Hz/穴位120×10=1200 条组合V/I记录/s;拆两条则2400条/s驱动支持的批量读、单机/整柜测量;协议能力未确认
10 Hz持续168 h1200×604800=725,760,000 条记录先确认是否必须全量长期存储;按原始与汇总分层
存储模型,100 B/组合样本约120,000 B/s、10.368 GB/天、72.576 GB/168 h(十进制)未计索引/WAL/文件系统;预留系数≥2的起点,实测后定盘
单Broker,40穴位示例400组合样本/s;共享协议会话可能低于此能力对每台8通道PSU测全通道读回耗时
单PSU利用率ρ=Σ(命令到达率×服务时间);建议持续ρ≤0.6–0.7起测80次通道读/s×10 ms=0.8,已超建议余量;需批量或降采样
Broker排队预算start deadline示例5 s;AtomicSequence示例3 s分别统计Queue/Driver/DB/IPC延迟,不能将TTL作为排队预算
UI刷新聚合2–5 Hz,采样独立;只呈现最新值与质量120穴位、报警风暴、窗口切换,观察帧阻塞和内存
续期负载120个通道若每8s续一次≈15行/s;应按组事务聚合与Task Journal及采样库隔离;父Permit和事件另计
CAN预算Σ帧率×实际位数/有效总线速率,含仲裁、填充、重发和UDS型号、位速率、FD数据段与总线数TBD;不能凭120穴位直接承诺

建议按小时/日分片或批量列式文件归档,不让单个 7 亿行 SQLite 表无限增长;保持 Run 索引与分片 Hash 可追溯。原始 CAN 帧与解码信号按追溯等级选择保留,质量事件与故障前后窗口优先。采样写入背压达到上限时按 Recipe 策略暂停/报警;不得静默丢样后仍判定数据完整。

性能门禁建议:控制心跳处理 p99<1 s、普通 IPC 本机空载往返 p95<20 ms、120穴位 UI 无长时间冻结;这些均为目标起点,必须列出 CPU/内存/磁盘/驱动/负载条件实测。周期帧和 UDS 时序用硬件时间戳验证。

19 安装、观测与运维

19.1 打包与目录

部署目录示例
C:\Program Files\AgingPlatform\        # 只读应用/签名驱动包
  Hmi\  Worker\  Broker\  Supervisor\
C:\ProgramData\AgingPlatform\Station01\
  config\{hash}\manifest.json         # 不可变发布快照
  system\aging_system.db
  local\arb.db
  broker\B01\broker-b01.db
  broker\B02\broker-b02.db
  broker\B03\broker-b03.db
  runs\{RunId}\...                    # 数据分片/报告索引
  logs\  recovery\  backups\

生产数据使用服务账户可写的 ProgramData 或独立数据盘,不把原型的 %LOCALAPPDATA%\PluginIntegrationTest 作为正式多账户共享路径。所有插件从统一配置服务取得路径;禁止各插件自行拼接不同 BaseFolder。

上线前冻结 x86/x64、SDK版本、.NET依赖和包 Hash。Broker 可采用 Windows Service;若 SDK 需要交互桌面,则使用受控用户进程并验证服务会话差异。驱动隔离范围由 SDK 全局状态决定,不能将一张卡的全局句柄分到多个 Broker。

19.2 观测与告警

统一 CorrelationId=Run/DUT/Step/Task;日志记录 BrokerId、epoch、ResourceKey、SessionKey、LeaseId 摘要、generation、ConfigHash、QueueWait、IoLatency、RetryCount、ReadbackQuality。运行指标包括活跃/隔离资源数、心跳余量、队列深度、Unknown任务、Outbox积压、数据Gap、DB busy时间、磁盘剩余、重启次数与CAN bus-off。

告警必须带影响穴位和处理入口,区分硬件联锁、通信故障、仲裁冲突和维护中;不能将维护占用持续刷成设备故障。诊断导出包含版本、匿名化日志、任务状态和恢复证据,排除凭据。备份使用 SQLite 一致性备份方式,不能仅复制主 .db 而忽略活动 WAL。

19.3 升级与回退

停止新Run → 排空任务/撤销写权 → 确认安全态 → 一致性备份 → 数据迁移 → 升级宿主及客户端 → 协议握手/自检 → 配置Hash一致 → 恢复生产。若 schema 不向后兼容,不能直接回退旧二进制;使用验证过的备份恢复并提升 epoch,保留升级期间日志及结果。

20 典型运行场景与方案原理

以下用实际调用过程说明各层为什么存在。场景中的编号、时间和电压均为解释示例,不是设备的已确认工艺参数。

20.1 两个 Worker 操作同一台多通道电源

W01 的 S020 与 W02 的 S021 分别映射到 PSU03/CH4、PSU03/CH5。两者各持有本通道的排他租约和 PSU03 的父共享 Permit,因此可同时处于“有权使用”状态。两条操作均交给 B01;B01 的 PSU03 SessionGate 先完整执行 CH4 的“选择通道→设定→读回”,再执行 CH5。通道授权并行、物理会话串行,因此无需反复打开连接,也不会把 CH4 的设定写到 CH5。

若有人要执行 PSU03 整机 Reset,它要求父独占;CH4 或 CH5 任一 Permit 仍在使用时都不能执行。这样既保留通道独立性,又覆盖整机命令的影响范围。

20.2 独立 TCP 电源走 Local

PSU024 固定归属于 W02。Recipe 通过 S024/Power 找到 LocalExecutor;W02 持有连接守卫、通道租约和会话队列,直接调用驱动。HMI 监控只读取 W02 发布的状态缓存。维护请求经 W02 的受控入口完成;若要换宿主,必须先停止 W02 的旧连接并通过恢复检查。Local 的价值是减少代理跳转和独立进程数量,代价是不能仅凭租约过期进行跨进程自动接管。

20.3 维护工程师要操作正在生产的通道

HMI 显示该通道由 Run-A/S024 使用。申请维护不会立即得到写权限:RunService 先让受影响 DUT 到达安全边界,资源转为 Draining;宿主停止接收新普通写操作,排空当前片段,执行适用安全动作并读回。确认旧连接/任务已隔离后,旧租约释放,新的维护 Grant 才能签发。交接超时会停在 RecoveryRequired;“等了30秒”本身不能成为接管依据。

20.4 Broker 已发命令,但客户端没收到结果

客户端使用同一 IdempotencyKey 查询或重提。若 Journal 已有终态,返回原结果;若只记录 Running,且无法判断硬件是否执行,则返回 Unknown 并核对读回,不能生成新请求再触发一次。这说明 Task Journal 解决“请求是否见过、结果是否保存”,而不是替硬件实现数据库事务。

20.5 Broker 重启后如何恢复

B01 重启先提升 epoch,旧 Grant 和旧排队任务失效,PSU01–05 暂时不可写。宿主重新识别设备、读取输出和协议状态;检查合格后资源回到 Free/Ready。W01/W02 必须重新申请租约,RunService 根据中断窗口决定继续还是重测。B02/B03 的独立 PSU 域可继续运行;若故障涉及共用温箱/PLC,则按真实共享影响范围处理,不能假设只有40穴位受影响。

20.6 120 个 DUT 等待与单次总线采集

120 个 DUTContext 各自保存步骤、SN、计时和结果,但等待使用异步计时,不对应120条轮询线程。同一 CAN 总线只由一个接收器读取,再解码并向多个 DUT 分发。Broker 的物理采集负责“数据从哪里来、是否新鲜”,Recipe 引擎负责“这个 DUT 是否达到工艺条件”,从而避免120次读取同一总线。

21 方案适用条件与限制

方案是否成立取决于物理拓扑和设备协议。以下条件直接决定 Local/Broker 路由、资源粒度及恢复行为;它们是方案边界,不是研发排期。

条件对方案的影响未明确时的处理
PSU 为120×1还是15×8前者可固定Local归属;后者跨Worker共享会话宜Broker保留两种拓扑,禁止混用映射公式
设备是否允许多个会话、全局命令影响哪些通道决定PhysicalSessionKey、父子资源和SessionGate范围按更大的冲突域保守隔离
CAN/SDK是否单实例、线程安全域与UDS地址决定一个Broker能持有什么、哪些任务可并行不拆分同一全局SDK句柄;地址冲突拒绝发布
温箱/PLC安全态与人员作业许可决定谁可控制设定、交接读回与独立断能动作不以软件TTL或令牌代替硬件安全条件
动作能否取消、是否幂等、能否可靠读回决定超时是可重试还是Unknown/恢复检查不可逆动作默认不自动重放
采样率、保留周期和总线实际能力决定队列、批量采集、数据分片与磁盘容量将容量数字视作负载假设,实测后配置
Recipe允许的暂停、恢复与计时规则决定掉线后续跑、步骤重入还是整件重测保持中断证据,不自动判定合格
多机共享与第三方直连决定是否必须Broker、TLS和网络访问控制禁止共享SMB上的arb.db,禁止绕过唯一宿主
6Worker/3Broker是否符合真实SDK故障域决定进程分组能否达到预期隔离效果进程数作为建议;共享硬件按真实影响范围处理

本文可以说明仲裁和恢复规则,但不能单独证明设备已达到节拍、硬实时、物理安全或168 h长稳要求。默认时间、队列容量和采样率均是可配置的初始假设;工程师发布配置时应填入实际设备参数。

22 来源、交付范围与阅读说明

本 HTML 所有样式、脚本与架构图均内置,打开与打印不依赖网络。全文含 22 章、3 张可放大架构/状态/部署图、完整 120 穴位映射、C# 合约、SQLite DDL、故障恢复矩阵和典型运行场景。附带原生 Draw.io 文件供工程编辑;HTML 内也可下载同一原生图及 SQL / C# 合约。

绘图规则:C:\Users\xcw11.HOMEAI\.codex\skills\drawio-default-style\SKILL.md,版本 1.0.0;同时遵循 Draw.io 插件原生格式/导出规则。图示内容为目标设计,静态实现事实以第 2 章为准。图 2 的安全确认包括在途命令终止、连接隔离、设备读回与必要的硬件作业许可。

参考技术资料(2026-11-03核对):[E1] SQLite Transaction:写事务与 BEGIN IMMEDIATE;[E2] SQLite WAL:单机约束与读写并发边界;[E3] Microsoft Named Pipes:进程间管道与身份能力。本文 IPC 帧、仲裁、队列、恢复流程是本项目设计选择,并非这些文档提供的完整方案。

来源路径用途SHA256(本次读取)
S1D:\TestPlatform\Aging_Software_Design_V2.1 (11).docxV2.1设计基线:Run/DUT、120穴位、仪器管理与数据分层36306c77933800fa0a9bd9719a77178021b44162d5034d244c8e3405d78238db
S2D:\TestPlatform\TapProject\docs\MultiProcess_Instrument_Resource_Solution.html旧多进程方案;本次对安全/IPC结论作修订b8e1ec02155baca47e61fcdeaf4e36c0086c73fa658a7954ee92801037bb12ac
S3D:\TestPlatform\TapProject\docs\DeviceConfig_DeviceDebug_Architecture.html配置、维护会话与调试审计设计c64f342e88828913b75c8813b69051028b5670e27e028ebb78e6bc732cdb80c4
S4D:\TestPlatform\TapProject\Aging.Infrastructure\Core\ResourceManager.cs当前进程内预约/代号/操作租约实现61e75bb59c690e8bd5ff1d2b6672bdaa72693dbf27c324adad03d23ed03f8319
S5D:\TestPlatform\TapProject\Aging.Infrastructure\Contracts\ResourceOwnerToken.csOwnerToken当前字段和构造行为c45b12b4843019b6ddca7c267d8fec962971fd9fdd35903ff5f1b8a08e6ad09d
S6D:\TestPlatform\TapProject\Aging.Infrastructure\Services\DeviceService.cs命令校验及驱动调用路径72a95fe5ddd07e5336b5a9b0e00c34d7ecf9abf104edd511956218bae69a1357
S7D:\TestPlatform\TapProject\Aging.Infrastructure\Services\AutoDeviceFacade.cs预约作用域及释放错误处理67b7521a593b5961bf70bf998c72806e8e62d63afb41969d2058c7ec522e8e19
S8D:\TestPlatform\TapProject\ConnectionUtil\PipeClient.csIPC响应不可达、serverName行为b17451398fdffd7216c7b4c4db55f541cee76175b66ae93d822964bf59311870
S9D:\TestPlatform\TapProject\ConnectionUtil\PipeServer.cs现有命名管道服务端基础58a3fc4f6f6ef8fe69a2781e429a557f0d529104717eb90ee10c7fdbb6d0b94e
S10D:\TestPlatform\TapProject\Plugin.DeviceConfig\Model\DeviceConfigModel.cs现有表与映射、版本模型4b0022da848a9c0033ae20595e7c93123b952b15096035151f2d3b824f20d980
S11D:\TestPlatform\TapProject\Aging.Infrastructure\Aging.Infrastructure.csprojnet472与依赖配置f39ad8e25368431e2d2d77c2488c03c027dae5b0812e9dd50ec75de36269cace
S12D:\06-产品开发\18-舱驾控制器老化设备\老化软件详细开发说明书_V1.0.docx本工作区既有详细开发说明参考9c0053ef959a55a15adcf06a7e27ba7cde46ae6f78ecb24291157c49b2d9cef7

本文不包含已完成的生产代码、真实设备配置或现场验收结论。方案适用条件和未确认的设备参数在第 21 章列出。